package br.com.bco.hibernate;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;

/**
 * UserType repons�vel por tipar o atributo mapeado no Hibernate com o c�digo de uma
 * Enumera��o(Integer) que implemente a interface {@link EnumCodigoDescricao}.
 * 
 * @param <E> enumera��o
 */
public class EnumIntegerUserType<E extends Enum<E>> implements UserType, ParameterizedType {

    private static final int[] SQL_TYPES = new int[] {Types.SMALLINT};
    private Class<E> clazz;
    private EnumCodigoDescricao enumCodigoDescricao;

    /**
     * Configura a enum.
     * 
     * @param params enum
     */
    @SuppressWarnings("unchecked")
    public void setParameterValues(Properties params) {
        String enumClassName = params.getProperty("enumClassName");
        if (enumClassName == null) {
            throw new MappingException("enumClassName parameter not specified");
        }
        try {
            this.clazz = (Class<E>) Class.forName(enumClassName);
        } catch (java.lang.ClassNotFoundException e) {
            throw new MappingException("enumClass " + enumClassName + " not found", e);
        }
    }

    /**
     * Assemble.
     * 
     * @param cached cached
     * @param owner owner
     * @throws HibernateException exception
     */
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }

    /**
     * DeepCopy.
     * 
     * @param value value
     * @throws HibernateException exception
     */
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    /**
     * Disassemble.
     * 
     * @param value value
     * @throws HibernateException exception
     */
    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    /**
     * Equals.
     * 
     * @param x x
     * @param y y
     * @throws HibernateException exception
     */
    public boolean equals(Object x, Object y) throws HibernateException {
        if (x == y) { //NOPMD by deinf.dejalma - Implementa��o de infra. Precisa testar que os objetos s�o o mesmo.
            return true;
        }
        if (null == x || null == y) {
            return false;
        }
        return x.equals(y);
    }

    /**
     * HashCode.
     * 
     * @param x x
     * @throws HibernateException exception
     */
    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }

    /**
     * IsMutable.
     */
    public boolean isMutable() {
        return false;
    }

    /**
     * Retorna a enum.
     * 
     * @param codigo
     * @return enum
     */
    private EnumCodigoDescricao getEnum(Integer codigo) {
        Class<? extends Enum<E>> enumClass = returnedClass();
        Enum<E>[] enums = enumClass.getEnumConstants();
        for (int i = 0; i < enums.length; i++) {
            if (((EnumCodigoDescricao) enums[i]).getCodigo() == codigo) {
                return (EnumCodigoDescricao) enums[i];
            }
        }
        return null;
    }

    /**
     * NullSafeGet.
     * 
     * @param rs rs
     * @param names []names
     * @param owner owner
     * @return object
     * @throws HibernateException - exception
     * @throws SQLException - exception
     */
    public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
            throws HibernateException, SQLException {
        Integer codigo = rs.getInt(names[0]);
        EnumCodigoDescricao enumObject = null;
        enumObject = getEnum(codigo);
        return enumObject;
    }

    /**
     * NullSafeSet.
     * 
     * @param st st
     * @param value valur
     * @param index index
     * @throws HibernateException - exception
     * @throws SQLException - exception
     */
    public void nullSafeSet(PreparedStatement st, Object value, int index)
            throws HibernateException, SQLException {
        if (null == value) {
            st.setNull(index, Types.INTEGER);
        } else {
            enumCodigoDescricao = (EnumCodigoDescricao) value;
            st.setInt(index, enumCodigoDescricao.getCodigo());
        }
    }

    /**
     * Replace.
     * 
     * @param original original
     * @param target target
     * @param owner owner
     * @return object
     * @throws HibernateException - exception
     */
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }

    /**
     * RetunedClass.
     * 
     * @return clazz
     */
    public Class<E> returnedClass() {
        return clazz;
    }

    /**
     * SqlTypes.
     * 
     * @return int[]
     */
    public int[] sqlTypes() {
        return SQL_TYPES.clone();
    }

}
